package au.edu.qut.bpm.genbinder.util;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.deckfour.xes.extension.std.XConceptExtension;
import org.deckfour.xes.extension.std.XLifecycleExtension;
import org.deckfour.xes.extension.std.XOrganizationalExtension;
import org.deckfour.xes.extension.std.XTimeExtension;
import org.deckfour.xes.model.XEvent;
import org.deckfour.xes.model.XLog;
import org.deckfour.xes.model.XTrace;

import au.edu.qut.bpm.genbinder.models.AbstractLog;
import au.edu.qut.bpm.genbinder.models.Activity;
import au.edu.qut.bpm.genbinder.models.Case;
import au.edu.qut.bpm.genbinder.models.ConcreteEventLog;
import au.edu.qut.bpm.genbinder.models.Resource;
import au.edu.qut.bpm.genbinder.models.Task;

public class XESToLogConverter {
	public static ConcreteEventLog createLog(XLog xlog) throws Exception {
		String logName = XConceptExtension.instance().extractName(xlog);
		AbstractLog alog = new AbstractLog(logName == null ? "null" : logName);
		ConcreteEventLog clog = new ConcreteEventLog(alog);

		for (XTrace xtrace : xlog) {
			String caseName = XConceptExtension.instance().extractName(xtrace);
			long traceTime = XTimeExtension.instance().extractTimestamp(xtrace.get(0)).getTime();

			Case oCase = new Case(caseName, alog, traceTime, xtrace.getAttributes());
			List<Activity> naturalOrder = new ArrayList<Activity>();

			boolean[] completeEventConsumed = new boolean[xtrace.size()];
			for (int pos = 0; pos < xtrace.size(); pos++)
				completeEventConsumed[pos] = false;
			long lastTimeStampSeen = -1;

			for (int pos = 0; pos < xtrace.size(); pos++) {
				Activity activity = null;

				XEvent event = xtrace.get(pos);
				String eventName = XConceptExtension.instance().extractName(event);
				String transition = XLifecycleExtension.instance().extractTransition(event);
				String resource = XOrganizationalExtension.instance().extractResource(event);
				Set<Resource> resourceSet = new HashSet<Resource>();
				resourceSet.add(new Resource(resource));
				String eventId = caseName + "_" + pos + "_" + eventName;
				long eventTime = XTimeExtension.instance().extractTimestamp(event).getTime();

				if (eventTime < lastTimeStampSeen)
					throw new Exception("Found a trace not ordered to timestamp: " + eventTime + " < " + lastTimeStampSeen);

				if ("start".equals(transition)) {
					boolean completeFound = false;
					for (int nextPos = pos + 1; nextPos < xtrace.size(); nextPos++) {
						XEvent nextEvent = xtrace.get(nextPos);
						String nextEventName = XConceptExtension.instance().extractName(nextEvent);
						String nextTransition = XLifecycleExtension.instance().extractTransition(nextEvent);
						//String nextResource = XOrganizationalExtension.instance().extractResource(nextEvent);
						long nextEventTime = XTimeExtension.instance().extractTimestamp(nextEvent).getTime();
						if (!nextEventName.equals(eventName))
							continue;
						if (!nextTransition.equals("complete"))
							continue;
						if (completeEventConsumed[nextPos])
							continue;
						activity = new Activity(eventId, "name", oCase, new Task(eventName), eventTime, nextEventTime - eventTime, resourceSet, event.getAttributes());
						clog.setActivityStart(activity, eventTime);
						clog.setActivityResources(activity, resourceSet);
						//System.out.println("1" + eventTime + resourceSet);
						//clog.boundStartTime.put(activity, eventTime);
						//clog.boundResources.put(activity, resourceSet);
						completeFound = true;
						completeEventConsumed[nextPos] = true;
						break;
					}
					if (!completeFound)
						activity = new Activity(eventId, "name", oCase, new Task(eventName), eventTime, 0L, resourceSet, event.getAttributes());
					clog.setActivityStart(activity, eventTime);
					clog.setActivityResources(activity, resourceSet);
					//System.out.println("2" + eventTime + resourceSet);
					//clog.boundStartTime.put(activity, eventTime);
					//clog.boundResources.put(activity, resourceSet);

				} else if ("complete".equals(transition)) {
					if (completeEventConsumed[pos] == false) {
						//System.out.println("lala-"+ event.getAttributes());
						activity = new Activity(eventId, "name", oCase, new Task(eventName), eventTime, 0L, resourceSet, event.getAttributes());
						clog.setActivityStart(activity, eventTime);
						clog.setActivityResources(activity, resourceSet);
						//System.out.println("3" + eventTime + resourceSet);
						//clog.boundStartTime.put(activity, eventTime);
						//clog.boundResources.put(activity, resourceSet);
						completeEventConsumed[pos] = true;
					}
				} else {
					throw new Exception("Found a lifecycle transition not equal to start or complete.");
				}

				if (activity != null) {
					oCase.getActivities().add(activity);
					naturalOrder.add(activity);
					alog.getTasks().add(new Task(eventName));
					if (resource != null) {
						if (!alog.getTaskResourceSetMap().containsKey(new Task(eventName)))
							alog.getTaskResourceSetMap().put(new Task(eventName), new HashSet<Set<Resource>>());
						alog.getTaskResourceSetMap().get(new Task(eventName)).add(resourceSet);
						alog.getResources().add(new Resource(resource));
					}
				}
			}

			// Impose a full ordering when importing from XES
			for (int i = 0; i < naturalOrder.size() - 1; i++) {
				Set<Activity> activitiesAfter = new HashSet<Activity>();
				for (int j = i + 1; j < naturalOrder.size(); j++)
					activitiesAfter.add(naturalOrder.get(j));
				oCase.getPartialOrder().put(naturalOrder.get(i), activitiesAfter);
			}
			alog.getCases().add(oCase);
		}
		///*
		Map<Task, Set<Set<Resource>>> resourceSetCanDoTask = alog.getTaskResourceSetMap();
		Set<Resource> resourceSetManager1 = new HashSet<Resource>();
		Set<Resource> resourceSetManager2 = new HashSet<Resource>();
		Set<Resource> resourceSetManager3 = new HashSet<Resource>();
		Set<Resource> resourceSetManager4 = new HashSet<Resource>();
		resourceSetManager1.add(new Resource("IS1"));
		resourceSetManager2.add(new Resource("IS2"));
		resourceSetManager3.add(new Resource("BS1"));
		resourceSetManager4.add(new Resource("BS2"));
		
		List<String> ISList = new ArrayList<String>();
		List<String> BSList = new ArrayList<String>();
		
		ISList.add("NCU");
		ISList.add("ARP");
		ISList.add("RAR");
		ISList.add("AVD");
		ISList.add("AVC");
		ISList.add("APT");
		ISList.add("PVA");
		ISList.add("ATT");
		ISList.add("CAR");
		
		BSList.add("RPV");
		BSList.add("AWO");
		BSList.add("ASD");
		BSList.add("CWR");
		BSList.add("RCV");
		BSList.add("ERC");
		BSList.add("SIN");
		
		for (Map.Entry<Task, Set<Set<Resource>>> entry : resourceSetCanDoTask.entrySet())
		{
//			System.out.println(entry.getKey().toString());
			if (ISList.contains(entry.getKey().toString())) {
				resourceSetCanDoTask.get(entry.getKey()).add(resourceSetManager1);
				resourceSetCanDoTask.get(entry.getKey()).add(resourceSetManager2);
			} else if (BSList.contains(entry.getKey().toString())) {
//				System.out.println(resourceSetCanDoTask.get(entry.getKey()));
				resourceSetCanDoTask.get(entry.getKey()).add(resourceSetManager3);
				resourceSetCanDoTask.get(entry.getKey()).add(resourceSetManager4);
			}
//			System.out.println(entry.getKey() + "====" + resourceSetCanDoTask.get(entry.getKey()));
		}

		alog.setTaskResourceSetMap(resourceSetCanDoTask);

		clog.setAbstractLog(alog);
		//*/
		return clog;
	}

}
